use std::{
    collections::HashMap,
    sync::{Arc, Mutex},
};

use monitor::{CompleterSuggestion, Monitor, MonitorCommand};

use super::{mon_command_completer, pwd::do_pwd};
use crate::{
    inode::{inode_root, link_path_walk, set_current_path},
    DfsError, DfsResult,
};

struct CdCommand {
    prev_path: Mutex<String>,
}

impl CdCommand {
    fn do_cd(&self, args: &HashMap<String, String>) -> DfsResult<()> {
        let mut lock = self.prev_path.lock().unwrap();

        if args.is_empty() {
            *lock = do_pwd();
            set_current_path(inode_root()).unwrap();
        } else {
            let path = args.get("path").unwrap();
            if path == "-" {
                let this = do_pwd();
                let prev = link_path_walk(&lock).ok_or(DfsError::NoDir)?;
                set_current_path(prev).unwrap();
                *lock = this;
            } else {
                let inode = link_path_walk(path).ok_or(DfsError::NoEnt)?;
                if inode.is_file() {
                    return Err(DfsError::IsFile);
                }
                *lock = do_pwd();
                set_current_path(inode).unwrap();
            }
        }

        Ok(())
    }
}

impl MonitorCommand for CdCommand {
    fn mon_protocol_exec(&self, args: &monitor::Protocol) -> DfsResult<monitor::Protocol> {
        self.do_cd(args)?;
        Ok(HashMap::new())
    }

    fn mon_readline_arg_type(&self) -> Option<&'static str> {
        Some("path:*?")
    }

    fn mon_readline_completer(&self, args: &[&str], is_space: bool) -> Option<CompleterSuggestion> {
        mon_command_completer(args.last(), true, is_space)
    }

    fn mon_readline_exec(
        &self,
        args: &HashMap<String, String>,
        _: &monitor::MonitorPrinter,
    ) -> DfsResult<()> {
        self.do_cd(args)
    }

    fn mon_readline_help(&self) -> String {
        String::from("改变当前工作目录")
    }

    fn mon_readline_long_help(&self) -> Option<String> {
        Some(String::from("cd  [dir]|[-]     - 改变当前工作路径到<dir>|返回上一个路径"))
    }
}

pub(crate) fn monitor_command_cd_register(mon: &Monitor) {
    mon.register_command("cd", Arc::new(CdCommand { prev_path: Mutex::new(String::from("/")) }))
        .unwrap();
}
